-- User
-- Create by weism
-- 玩家对象

User = User or {};
User.__index = User;

-- 构造函数
function User.new(dbase)
    local self = {};
    setmetatable(self, User);
    self.dbase = Dbase.new(dbase);
    self.items = {};
    self.pets = {};
    self.skills = {};
    self.achievements = {};
    self.equipments = {};
    self.tasks = {};
    self.signIn = {};
    self.talents = {};
    self.talentsOption = {};
    self.type = OBJECT_TYPE_USER,

    -- 对象为玩家类型
    self.dbase:set("type", OBJECT_TYPE_USER);

    -- 登记下映射关系
    self.rid = dbase.rid;
    RID.add(self.rid, self);

    -- 安装属性触发器
    AttribM.installTrigger(self);
    return self;
end

-- 取得该对象的描述字符串
function User:get_ob_id()
    return string.format("User:%s", tostring(self.rid));
end

-- 析构
function User:destroy()
    RID.remove(dbase.rid);
    self.dbase = {};
end

-- 获取rid
function User:getRid()
    return self.rid;
end

-- 名字
function User:getName()
    return self.dbase:query("name", "anonymous");
end

-- 玩家当前经验
function User:getExp()
    return self.dbase:query("exp", 0);
end

-- 玩家当前血量
function User:getHp()
    Profiler.funcBegin("User:getHp");

    local hp = self:queryAttrib("hp");

    -- 增加抵挡伤害
    local value = PropM.getShieldHit();
    if value > 0 then
        hp = hp + value;
    end

    -- 不要小于0
    hp = iif(hp >= 0, hp, 0);

    Profiler.funcEnd("User:getHp");
    return hp;
end

-- 计算最大血量
function User:getMaxHp(combat)
    Profiler.funcBegin("User:getMaxHp");
    local maxHp = self:queryAttrib("max_hp");
    Profiler.funcEnd("User:getMaxHp");
    return maxHp;
end

-- 计算最大魔法值
function User:getMaxMp()
    return self:queryAttrib("max_mp");
end

-- 玩家当前魔法值
function User:getMp()
    local mp = self:queryAttrib("mp");

    -- 不要小于0
    mp = iif(mp >= 0, mp, 0);

    return mp;
end

-- 玩家当前探索点
function User:getSp()
    return self:queryAttrib("search_point");
end

-- 计算攻击值
function User:getAttack()
    Profiler.funcBegin("User:getAttack");
    local attack = self:queryAttrib("attack");
    Profiler.funcEnd("User:getAttack");
    return attack;
end

-- 命中
function User:getAccuracy()
    Profiler.funcBegin("User:getAccuracy");
    local accuracy = self:queryAttrib("accuracy");
    Profiler.funcEnd("User:getAccuracy");
    return accuracy;
end

-- 闪避
function User:getDodge()
    Profiler.funcBegin("User:getDodge");
    local dodge = self:queryAttrib("dodge");
    Profiler.funcEnd("User:getDodge");
    return dodge;
end

-- 计算魔力
function User:getMagic()
    -- TODO
    return self:queryAttrib("magic");
end

-- 计算最大体力值
function User:getMaxLife()
    -- 公式算
    return self:queryAttrib("max_life");
end

-- 获取玩家所有物品
function User:getItems()
    return self.items;
end

-- 获取出战英雄
function User:getActivePet()
    local activePet = self.dbase:query("active_pet", 0);
    return self.pets[activePet];
end

-- 所有技能
function User:getAllSkills(onlyCareer)
    -- 通过出战宠物获取技能
    local pet = self:getActivePet();
    if not pet then
        return {};
    end

    return pet:getAllSkills(onlyCareer);
end

-- 技能等级
function User:getSkillLevel(skillId)
    local pet = self:getActivePet();
    local special = 0;

    if pet then
        special = pet:getSpecial();
    end

    if special > 0 and SkillM.findBaseSkill(special) == SkillM.findBaseSkill(skillId) then
        return SkillM.query(FormulaM.invoke("CALC_FINAL_SKILL", ME.user, special), "level");
    end

    local lictorPets = self.dbase:query("lictor_pets");

    if not lictorPets then
        return 0;
    end

    for _, classId in pairs(lictorPets) do
        local pet = self.pets[classId];
        if pet then
            local special = pet:getSpecial() or 0;
            if SkillM.findBaseSkill(special) == SkillM.findBaseSkill(skillId) then
                return SkillM.query(FormulaM.invoke("CALC_FINAL_SKILL", pet, special), "level");
            end
        end
    end

    return 0;
end

-- 获得所有宠物ID
function User:getAllPetIds()
    local list = {};
    for _, pet in pairs(self.pets) do
        table.insert(list, pet.classId);
    end
    return list;
end

-- 获取所有的宠物对象
function User:getAllPets()
    local list = {};
    for _, pet in pairs(self.pets) do
        table.insert(list, pet);
    end
    return list;
end

-- 所有佩戴的技能
function User:getSkillsOption()
    return self.skillOption or {};
end

-- 位置取0
function User:getPos()
    return self.pos or 0;
end

-- 获取玩家当前等级
function User:getLevel()
    return self.dbase:query("level", 1);
end

-- 获取玩家已激活的天赋
function User:getTalents()
    return self.talents or {};
end

-- 获取玩家已佩戴的天赋
function User:getTalentsOption()
    return self.talentsOption or {};
end

-- 设置天赋
function User:setTalents(talents)
    self.talents = talents;
end

-------------------------------
-- 设置出战英雄
-- @param classId 待出战英雄的编号
function User:setActivePet(classId)
    self.dbase:set("active_pet", classId);
end

-- 扣除属性
function User:costAttrib(attrib, amount, delayPlay)
    local curValue = self.dbase:query(attrib, 0);

    -- 扣除"最大生命值"的时候需要进行修正
    if attrib == "max_hp" then
        -- 扣除最大生命值时必须剩下的最大生命值>0
        local value = self:queryAttrib("max_hp") or 1;
        if value < amount then
            amount = value - 1;
        end
    end

    curValue = curValue - amount;
    self.dbase:set(attrib, curValue);

    -- 扣除
    self:attribCoding(attrib, -amount);

    -- 如果是探索点
    if attrib == "search_point" then
        local props = PropM.fetchProps(self, "get_gold");
        for _, prop in ipairs(props) do
            PropM.trigger(self, prop[1], prop[2], amount);
        end
    end

    -- 如果不需要延迟播放，直接抛出事件
    if not delayPlay then
        Profiler.funcBegin("USER_INFO_UPDATED")
        EventMgr.fire(event.USER_INFO_UPDATED, attrib);
        Profiler.funcEnd("USER_INFO_UPDATED")
    end

    -- 消耗属性
    EventMgr.fire(event.COST_ATTRIB, {[attrib] = amount, ["cost"] = attrib, ["attrib"] = attrib,});

    if attrib == "max_hp" then
        -- 扣除最大生命值的时候还需要一起扣除生命值
        local curHp = self:queryAttrib("hp", 0);
        local hpValue = math.min(amount, curHp - 1);

        if hpValue > 0 then
            self.dbase:set("hp", self.dbase:query("hp") - hpValue);

            -- 扣除
            self:attribCoding("hp", -hpValue);

            -- 如果不需要延迟播放，直接抛出事件
            if not delayPlay then
                EventMgr.fire(event.USER_INFO_UPDATED, "hp");
            end

            -- 消耗属性
            EventMgr.fire(event.COST_ATTRIB, {["hp"] = hpValue, ["cost"] = attrib});

            -- 抛出伤害事件
            EventMgr.fire(event.HERO_DAMAGED, {["damage"] = hpValue, });
        end
    end

    return true;
end

-- 增加mp属性 可以超过自身的mp最大值
function User:addAttribBySplit(attrib, amount)

    if attrib ~= "mp" then
        return;
    end

    local count = amount;
    if ME.user:getMaxMp() <= ME.user:getMp() then
        -- 提升一点
        count = 1;
    else
        -- 不能超出上限
        count = math.min(count, ME.user:getMaxMp() - ME.user:getMp());
    end

    local curValue = self.dbase:query(attrib, 0);
    ME.user.dbase:set(attrib, curValue + count);

    -- 加密属性
    self:attribCoding(attrib, count);

    EventMgr.fire(event.USER_INFO_UPDATED, attrib);
end

-- 获得属性
function User:addAttrib(attrib, amount, desc)
    local curValue = self.dbase:query(attrib, 0);

    -- 属性最大值限制
    local maxValue = AttribM.maxAttrib(ME.user, attrib);
    if curValue >= maxValue then
        amount = 0;
    else
        amount = math.min(amount, maxValue - curValue);
    end

    curValue = curValue + amount;

    self.dbase:set(attrib, curValue);

    -- 加密属性
    self:attribCoding(attrib, amount);

    EventMgr.fire(event.BONUS_ATTRIB, {
        ["attrib"] = attrib,
        ["count"]  = amount,
        ["desc"]   = desc,
        ["cost"]   = attrib,
    });

    EventMgr.fire(event.USER_INFO_UPDATED, desc);
end

-- 将属性简单加密，和一个随机数异或
function User:attribCoding(attrib, amount)
    local info = ME.user.dbase:query("attrib_coding", {});

    -- 不需要加密的属性
    if not SimpleEncryptM.query(attrib) then
        return;
    end

    -- 高位保存属性值（20bit，暂时够用）
    if nil == info[attrib] then
        amount = self.dbase:query(attrib, 0);

        info[attrib] = SimpleEncryptM.code(attrib, amount);
    else
        local v = info[attrib];

        v = SimpleEncryptM.decode(attrib, v);

        v = v + amount;

        info[attrib] = SimpleEncryptM.code(attrib, v);
    end

    ME.user.dbase:set("attrib_coding", info);
end

-- 查询属性
function User:queryAttrib(attrib, args)
    return AttribM.queryAttrib(self, attrib, args);
end

-- 设置当前血量
function User:setHp(hp)
    local curHp = self:queryAttrib("hp");

    self:costAttrib("hp", curHp - hp);
    -- local baseHp = self.dbase:query("hp");

    -- self.dbase:set("hp", baseHp + hp - curHp);
end

-- 设置当前魔法值
function User:setMp(mp)
    local curMp = self:queryAttrib("mp");
    self:costAttrib("mp", curMp - mp);
    -- local baseMp = self.dbase:query("mp");

    -- self.dbase:set("mp", baseMp + mp - curMp);
end

-- 收到伤害
function User:receiveDamage(damage, delayPlay)
    Profiler.funcBegin("User:receiveDamage");

    -- DEBUG_MODE开启无敌
    if DEBUG_MODE == 1 and ME.user.dbase:query("gd_invincible_buff") == 1 then
        return self:getHp();
    end

    if not self:costAttrib("hp", damage, delayPlay) then
        self.dbase:set("hp", 0);
    end

    local hp = self:getHp();

    local msg = string.format("[User]%s收到伤害：%d，当前hp：(%s/%s)，max_hp：(%s/%s)",
        self.dbase:query("name", ""), damage, hp, self.dbase:query("hp"),
        self:getMaxHp(), self.dbase:query("max_hp"));
    DungeonLogM.addLog(msg);

    -- 事件
    -- EventMgr.fire(event.HERO_DAMAGED, {["damage"] = damage, });

    Profiler.funcEnd("User:receiveDamage");
    return hp;
end

-- 玩家是否死亡了
function User:isDead()
    if self:queryAttrib("hp") <= 0 then
        return true;
    end
    return false;
end

-- 玩家死亡
function User:die(para)
    if self:getHp() > 0 then
        return false;
    end

    -- 判断一下是否限制了复活
    if not para or tonumber(para["no_revive"]) ~= 1 then
        local props = PropM.fetchProps(self, "die_skill");
        for _, prop in ipairs(props) do
            PropM.trigger(self, prop[1], prop[2]);
        end

        -- 尝试复活
        if PropM.trigger(self, "soul_to_revive", 500) then
            DungeonLogM.addLog("[User]玩家消耗灵魂死亡复活。");

            return false;
        end

        -- 尝试复活
        props = PropM.fetchProps(self, "revive2");
        for _, prop in ipairs(props) do
            if PropM.trigger(self, prop[1], prop[2]) then
                DungeonLogM.addLog("[User]玩家死亡复活。");

                return false;
            end
        end

        props = PropM.fetchProps(self, "revive_equip");
        for _, prop in ipairs(props) do
            if PropM.trigger(self, prop[1], prop[2]) then
                DungeonLogM.addLog("[User]玩家死亡复活。");

                return false;
            end
        end

        props = PropM.fetchProps(self, "revive_skill");
        for _, prop in ipairs(props) do
            if PropM.trigger(self, prop[1], prop[2]) then
                DungeonLogM.addLog("[User]玩家死亡复活。");

                return false;
            end
        end

        -- 尝试复活
        if PropM.trigger(self, "revive", 1) then
            DungeonLogM.addLog("[User]玩家死亡复活。");

            -- 把神圣重生的属性删除
            CombatStatusM.clearStatus(self, "holy_revive");

            return false;
        end
    end

    DungeonLogM.addLog("[User]你死亡了");

    -- 抛出玩家死亡事件
    EventMgr.fire(event.HERO_DIE, para);

    return true;
end

-- 玩家复活
function User:revive(hp)
    hp = hp or self.dbase:query("max_hp", 0);

    local msg = string.format("[User]你复活了，当前血量%s，最大血量%s", hp, self:getMaxHp());
    DungeonLogM.addLog(msg);

    -- 恢复血量
    local add = hp - self.dbase:query("hp", 0);
    self:addAttrib("hp", add, "revive");
    -- self.dbase:set("hp", hp);

    -- 恢复魔法
    self:addAttrib("mp", self.dbase:query("max_mp") - self.dbase:query("mp"), "revive");
    -- self.dbase:set("mp", self.dbase:query("max_mp"));

    -- 清除负面状态
    CombatStatusM.clearDebuf(self);

    -- 抛出复活事件
    EventMgr.fire(event.HERO_REVIVE);
end

-- 获取已复活次数
function User:getReviveTimes()
    return self.dbase:query("revive_times", 0);
end

-- 设置已复活次数
function User:setReviveTimes(times)
    self.dbase:set("revive_times", times);
end

-- 获取已复活总次数
function User:getTotalReviveTimes()
    return self.dbase:query("total_revive_times", 0);
end

-- 设置已复活总次数
function User:setTotalReviveTimes(times)
    self.dbase:set("total_revive_times", times);
end

-- 学习技能
function User:learnSkill(skillId)
    -- 如果已经有此技能了
    if table.indexOf(self.skills, skillId) > 0 then
        return;
    end

    table.insert(self.skills, skillId);
end

-- 升级技能，这里不判断能否升级
function User:upgradeSkill(skillId)
    local skillInfo = SkillM.query(skillId);

    local pos = table.indexOf(self.skillOption, skillId);

    SkillM.refreshSkillProp(skillId);

    self.skillOption[pos] = skillInfo.next;
end

-- 更新任务数据
function User:setTasks(tasks)
    self.tasks = tasks;
end

-- 判断是否为已激活天赋
function User:isActivatedTalent(skillId)
    local talents = self.talents;
    for _, id in pairs(talents) do
        if skillId == id then
            return true;
        end
    end

    return false;
end

-- 获取英雄能提升的最大阶位
function User:canUpgradeMaxRank()
    local rank = 1;
    local ress = DungeonAreaM.getOpenResource();
    if #ress > 0 then
        for i = 1, #ress do
           local tempRank = ItemM.query(ress[i], "rank");
           if tempRank > rank then
               rank = tempRank;
           end
        end
    end
    return rank;
end

-- 获取携带的神器
function User:getCarryArtifact()
    return ME.user.dbase:query("carry_artifact", 0);
end

-- 判断是否支付得其
function User:canCost(cost)
    if not cost or #table.keys(cost) <= 0 then
        return true;
    end

    local ret = true;
    for _, key in ipairs(table.keys(cost)) do
        if type(key) == "string" then
            -- key为字符串，认为是属性
            if self:queryAttrib(key) < cost[key] then
                ret = false;
                break;
            end

        elseif type(key) == "number" then
            -- key为数值，认为是道具
            if ItemM.getAmount(self, key) < cost[key] then
                ret = false;
                break;
            end
        else
            print("未经许可的消耗类型")
        end
    end

    return ret;
end

-- 获取角色创建时间
function User:getRoleCreateTime()
    return math.ceil(self.dbase:query("first_login_time") or TimeM.getServerTime());
end
